home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Freeware 1998 June
/
SGI Freeware 1998 June.iso
/
dist
/
fw_UMINNgopher.idb
/
usr
/
freeware
/
src
/
gopher_1.12
/
gopherd
/
gopherd.c.z
/
gopherd.c
Wrap
C/C++ Source or Header
|
1997-09-09
|
38KB
|
1,635 lines
/********************************************************************
* $Author: drich $
* $Revision: 1.1 $
* $Date: 1995/10/03 04:08:31 $
* $Source: /proj/freeware1.0/gopher1.12/src/gopherd/RCS/gopherd.c,v $
* $State: Exp $
*
* Paul Lindner, University of Minnesota CIS.
*
* Copyright 1991, 1992 by the Regents of the University of Minnesota
* see the file "Copyright" in the distribution for conditions of use.
*********************************************************************
* MODULE: gopherd.c
* Routines to implement the gopher server.
*********************************************************************
* Revision History:
* $Log: gopherd.c,v $
* Revision 1.1 1995/10/03 04:08:31 drich
* gopher 1.2 check-in
*
* Revision 1.2.1.6 1993/01/09 02:37:29 lindner
* Added ftp gateway logging
* Changed gethostbyaddr() to work on UNICOS
*
* Revision 1.2.1.5 1993/01/05 21:32:25 lindner
* Fixed printfile() so that it deals with files with a period on a line
* all by itself correctly. Also removed one writestring() call.
*
* Revision 1.2.1.4 1993/01/05 21:10:38 lindner
* Removed setuid() call that broke when using -u and chroot()
*
* Revision 1.2.1.3 1992/12/22 21:55:30 lindner
* fixed typo, int was meant to be double.. Grrrr
*
* Revision 1.2.1.2 1992/12/22 21:48:15 lindner
* Added extern maxload, fixes bug with previous version...
*
* Revision 1.2.1.1 1992/12/21 20:51:52 lindner
* Moved loadrestrict stuff to kernutils.c, also the server
* can now restrict for load average from standalone mode.
*
* Revision 1.2 1992/12/13 06:13:59 lindner
* Added code to do readline timeouts <mtm>
*
* Revision 1.1 1992/12/10 23:13:27 lindner
* gopher 1.1 release
*
*
*********************************************************************/
/* Originally derived from an
* Example of server using TCP protocol
* pp 284-5 Stevens UNIX Network Programming
*/
#include "gopherd.h"
void LOGGopher();
static int uid = -2;
extern char *getdesc();
extern double maxload;
void Process_Side();
/* This function is called on a read timeout from the network */
#include <setjmp.h>
jmp_buf env;
SIGRETTYPE read_timeout()
{
longjmp(env,1);
}
/*
* This routine finds out the hostname of the server machine.
* It uses a couple of methods to find the fully qualified
* Domain name
*/
char *
GetDNSname(backupdomain)
char *backupdomain;
{
static char DNSname[MAXHOSTNAMELEN];
struct hostent *hp;
char *cp;
cp = GDCgetHostname(Config);
if (*cp != '\0')
return(cp);
DNSname[0] = '\0';
/* Work out our fully-qualified name, for later use */
if (gethostname(DNSname, MAXHOSTNAMELEN) != 0) {
fprintf(stderr, "Cannot determine the name of this host\n");
exit(-1);
}
/* Now, use gethostbyname to (hopefully) do a nameserver lookup */
hp = gethostbyname( DNSname);
/*
** If we got something, and the name is longer than hostname, then
** assume that it must the the fully-qualified hostname
*/
if ( hp!=NULL && strlen(hp->h_name) > strlen(DNSname) )
strncpy( DNSname, hp->h_name, MAXHOSTNAMELEN );
else
strcat(DNSname, backupdomain);
return(DNSname);
}
/*
* Tries to figure out what the currently connected port is.
*
* If it's a socket then it will return the port of the socket,
* if it isn't a socket then it returns -1.
*/
int GetPort(fd)
int fd;
{
struct sockaddr_in serv_addr;
int length = sizeof(serv_addr);
/** Try to figure out the port we're running on. **/
if (getsockname(fd, (struct sockaddr *) &serv_addr,&length) == 0)
return(ntohs(serv_addr.sin_port));
else
return(-1);
}
/*
* This function returns a socket file descriptor bound to the given port
*/
int
bind_to_port(port)
int port;
{
struct sockaddr_in serv_addr;
struct linger linger;
int reuseaddr = 1;
int sockfd;
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
err_dump("server: can't open stream socket");
/** Bind our local address so that the client can send to us **/
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(port);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) <0)
err_dump("server: can't bind local address");
linger.l_onoff = linger.l_linger = 0;
if (setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char *)&linger,
sizeof (linger)) < 0)
err_dump("server: can't turn off linger sockopt");
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseaddr,
sizeof(reuseaddr)) < 0)
err_dump("server: can't set REUSEADDR!");
return(sockfd);
}
void
main(argc, argv)
int argc;
char *argv[];
{
int childpid;
int sockfd, newsockfd;
int clilen;
struct sockaddr_in cli_addr;
boolean OptionsRead = FALSE;
/*** for getopt processing ***/
int c;
extern char *optarg;
extern int optind;
int errflag =0;
pname = argv[0];
strcpy(Data_Dir, DATA_DIRECTORY);
err_init(); /* openlog() early - before we chroot() of course */
/*** Check argv[0], see if we're running as gopherls, etc. ***/
RunServer = RunLS = RunIndex = FALSE;
if (strstr(argv[0], "gopherls") != NULL) {
RunLS = TRUE;
} else if (strstr(argv[0], "gindexd") != NULL) {
RunIndex = TRUE;
dochroot = FALSE;
} else
RunServer = TRUE; /** Run the server by default **/
Config = GDCnew(); /** Set up the general configuration **/
while ((c = getopt(argc, argv, "mCDIcL:l:o:u:U:")) != -1)
switch (c) {
case 'D':
DEBUG = TRUE;
break;
case 'I':
RunFromInetd = TRUE;
break;
case 'C':
Caching = FALSE;
break;
case 'm':
if (RunIndex)
MacIndex = TRUE;
break;
case 'c':
dochroot = FALSE;
if (!RunFromInetd) {
printf("Not using chroot() - be careful\n");
if ( getuid() == 0 || geteuid() == 0 )
printf("You should run without root perms\n");
}
break;
case 'L': /** Load average at which to restrict usage **/
maxload = atof(optarg);
break;
case 'l': /** logfile name **/
if (*optarg == '/')
GDCsetLogfile(Config, optarg);
else {
char tmpstr[256];
getwd(tmpstr);
strcat(tmpstr, "/");
strcat(tmpstr, optarg);
GDCsetLogfile(Config, tmpstr);
}
break;
case 'o': /** option file **/
if (*optarg == '/')
GDCfromFile(Config, optarg);
else {
char tmpstr[256];
getwd(tmpstr);
strcat(tmpstr, "/");
strcat(tmpstr, optarg);
GDCfromFile(Config, tmpstr);
}
OptionsRead = TRUE;
break;
case 'u':
{
struct passwd *pw = getpwnam( optarg );
if ( !pw ) {
fprintf(stderr,
"Could not find user '%s' in passwd file\n",
optarg);
errflag++;
} else {
uid = pw->pw_uid;
if (!RunFromInetd) {
printf("Running as user '%s' (%d)\n",
optarg, uid);
}
}
}
break;
case 'U': /* set uid to use */
uid = atoi( optarg );
if (!RunFromInetd) {
printf("Running using uid %d\n", uid);
}
break;
case '?':
case 'h':
errflag++;
break;
}
if (errflag) {
fprintf(stderr, "Usage: %s [-CDIc] [-u userid] [-U uid] [-s securityfile] [-l logfile] <datadirectory> <port>\n", argv[0]);
fprintf(stderr, " -C turns caching off\n");
fprintf(stderr, " -D enables copious debugging info\n");
fprintf(stderr, " -I enable \"inetd\" mode\n");
fprintf(stderr, " -c disable chroot(), use secure open routines instead\n");
fprintf(stderr, " -u specifies the username for use with -c\n");
fprintf(stderr, " -U specifies the UID for use with -c\n");
fprintf(stderr, " -o override the default options file '%s'\n", CONF_FILE);
fprintf(stderr, " -l specifies the name of a logfile\n");
exit(-1);
}
if (uid == -2)
uid = getuid(); /** Run as current user... **/
if ( uid == 0 && !RunFromInetd )
printf("Warning! You really shouldn't run the server as root!...\n");
if (optind < argc) {
strcpy(Data_Dir, argv[optind]);
optind++;
} else if (RunLS)
strcpy(Data_Dir, "/");
if (optind < argc) {
GopherPort = atoi(argv[optind]);
optind++;
}
/** Read the options in, if not overridden **/
if (OptionsRead == FALSE)
GDCfromFile(Config, CONF_FILE);
if (RunLS) {
Zehostname =GetDNSname(DOMAIN_NAME);
Caching = FALSE;
fflush(stdout);
uchdir(Data_Dir);
listdir(fileno(stdout), "/");
exit(0);
}
if (!RunFromInetd) {
printf("Gopher Server, Copyright 1991,92 the Regents of the University of Minnesota\n");
printf("See the file 'Copyright' for conditions of use\n");
printf("Data directory is %s\n", Data_Dir);
printf("Port is %d\n", GopherPort);
}
if (*GDCgetLogfile(Config) != '\0' && !RunFromInetd)
printf("Logging to File %s\n", GDCgetLogfile(Config));
/*
* Would like to setuid() here, but have to wait until after the
* bind() in case we're going to be running on a privileged port.
*/
if (uchdir(Data_Dir)) {
if (!RunIndex) {
fprintf(stderr, "Cannot change to data directory!! %s \n",Data_Dir);
exit(-1);
}
}
if (dochroot && getuid() != 0) {
fprintf(stderr, "Gopherd uses the privileged call chroot(). Please become root.\n");
exit(-1);
}
fflush(stderr);
fflush(stdout);
if (DEBUG == FALSE && RunFromInetd==FALSE)
daemon_start(0);
/*** Hmmm, does this look familiar? :-) ***/
err_init(); /* does this look familiar too?? :-) */
/** Ask the system what host we're running on **/
Zehostname = GetDNSname(DOMAIN_NAME);
if (RunFromInetd) {
/** Ask the system which port we're running on **/
int newport=0;
if ((newport =GetPort(0)) !=0)
GopherPort=newport;
/*** Do the stuff for inetd ***/
while(do_command(0)!=0); /* process the request */
exit(0);
}
/** Open a TCP socket (an internet stream socket **/
sockfd = bind_to_port(GopherPort);
/* have to setuid() here, in case we're using a privileged port */
/* if ( setuid(uid) != 0 )
err_sys("Cannot setuid(%d): ",uid);*/
listen(sockfd, 5);
for ( ; ; ) {
/*
* Wait for a connection from a client process.
* This is an example of a concurrent server.
*/
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < 0)
err_dump("server: accept error");
if ( (childpid = fork()) < 0)
err_dump("server: fork error");
else if (childpid == 0) { /* Child process */
close(sockfd); /* close original socket */
while(do_command(newsockfd)!=0); /* process the request */
exit(0);
}
/** clean up any zombie children **/
sig_child();
close(newsockfd); /* parent process */
}
}
/*
*
* Code stolen from nntp.....
*
* inet_netnames -- return the network, subnet, and host names of
* our peer process for the Internet domain.
*
* Parameters: "sock" is our socket
* "host_name"
* is filled in by this routine with the
* corresponding ASCII names of our peer.
*
* if there doesn't exist a hostname in DNS etal,
* the IP# will be inserted for the host_name
*
* "ipnum" is filled in with the ascii IP#
* Returns: Nothing.
* Side effects: None.
*/
void
inet_netnames(sockfd, host_name, ipnum)
int sockfd;
char *host_name;
char *ipnum;
{
struct sockaddr_in sa;
int length;
u_long net_addr;
struct hostent *hp;
length = sizeof(sa);
getpeername(sockfd, &sa, &length);
strcpy(ipnum, inet_ntoa(sa.sin_addr));
strcpy(host_name, inet_ntoa(sa.sin_addr));
hp = gethostbyaddr((char *) &sa.sin_addr,
sizeof (sa.sin_addr.s_addr), AF_INET);
if (hp != NULL)
(void) strcpy(host_name, hp->h_name);
}
/*
* This finds the current peer and the time and jams it into the
* logfile (if any) and adds the message at the end
*/
void
LOGGopher(sockfd, message)
int sockfd;
char *message;
{
static char host_name[256];
static char ipnum[256];
time_t Now;
char *cp;
/* cp + ' ' + host_name + ' : ' + MAXLINE + '\n' + '\0' */
char buf[286+MAXLINE];
struct flock lock;
host_name[0] = '\0';
if (LOGFileDesc != -1) {
if (sockfd > -1) {
inet_netnames(sockfd,host_name, ipnum);
}
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0L;
lock.l_len = 0L;
fcntl(LOGFileDesc, F_SETLKW, &lock);
time(&Now); /* Include this in the lock to make sure */
cp = ctime(&Now); /* log entries are chronological */
ZapCRLF(cp);
/* someone else may have written to the file since we opened it */
lseek(LOGFileDesc, 0L, SEEK_END);
sprintf(buf, "%s %d %s : %s\n", cp, getpid(), host_name, message);
write(LOGFileDesc, buf, strlen(buf));
/* unlock the file */
lock.l_type = F_UNLCK;
fcntl(LOGFileDesc, F_SETLKW, &lock);
if (DEBUG)
printf("%s %d %s : %s\n", cp, getpid(), host_name, message);
}
}
void
process_mailfile(sockfd, Mailfname)
int sockfd;
char *Mailfname;
{
FILE *Mailfile;
char Zeline[MAXLINE];
char outputline[MAXLINE];
char Title[MAXLINE];
long Startbyte=0, Endbyte=0, Bytecount=0;
boolean flagged = 0;
Mailfile = rfopen(Mailfname, "r");
if (Mailfile == NULL) {
Abortoutput(sockfd, "Cannot access file");
return;
}
while (fgets(Zeline, MAXLINE, Mailfile) != NULL) {
if (strncmp(Zeline, "Subject: ", 9)==0 && (!flagged)) {
flagged =1;
strcpy(Title, Zeline + 9);
ZapCRLF(Title);
if (DEBUG)
fprintf(stderr, "Found title %s", Title);
}
if (is_mail_from_line(Zeline)==0) {
Endbyte = Bytecount;
flagged =0;
if (Endbyte != 0) {
sprintf(outputline, "0%s\tR%d-%d-%s\t%s\t%d\r\n",
Title, Startbyte, Bytecount, Mailfname,
Zehostname, GopherPort);
if (writestring(sockfd, outputline) < 0)
LOGGopher(sockfd, "Client went away"), exit(-1);
Startbyte=Bytecount;
*Title = '\0';
}
}
Bytecount += strlen(Zeline);
}
if (*Title != '\0') {
sprintf(outputline, "0%s\tR%d-%d-%s\t%s\t%d\r\n",
Title, Startbyte, Bytecount, Mailfname,
Zehostname, GopherPort);
if (writestring(sockfd, outputline)<0)
LOGGopher(sockfd, "Client went away"),exit(-1);
}
if (writestring(sockfd, ".\r\n")<0)
LOGGopher(sockfd, "Client went away"),exit(-1);
}
boolean
Can_Read(sockfd)
int sockfd;
{
int length;
char host_name[256];
char ip[256];
char *cp;
inet_netnames(sockfd, host_name, ip);
return(GDCCanRead(Config, host_name, ip));
}
boolean
Can_Browse(sockfd)
int sockfd;
{
int length;
char host_name[256];
char ip[256];
char *cp;
inet_netnames(sockfd, host_name, ip);
return(GDCCanBrowse(Config, host_name, ip));
}
boolean
Can_Search(sockfd)
int sockfd;
{
int length;
char host_name[256];
char ip[256];
char *cp;
inet_netnames(sockfd, host_name, ip);
return(GDCCanSearch(Config, host_name, ip));
}
int
do_command(sockfd)
int sockfd;
{
char inputline[MAXLINE];
int length; /* Length of the command line */
char logline[MAXLINE];
char *selstr;
/*** Reopen the log file ***/
if (*GDCgetLogfile(Config) != '\0') {
LOGFileDesc = uopen(GDCgetLogfile(Config), O_WRONLY | O_APPEND |O_CREAT, 0644);
if (LOGFileDesc == -1) {
printf("Can't open the logfile: %s\n", GDCgetLogfile(Config));
exit(-1);
}
}
if(LoadTooHigh()) {
LOGGopher(sockfd, "System Load Too High.");
Abortoutput(sockfd, "System is too busy right now. Please try again later.");
exit(-1);
}
/*** Make sure we do a tzset before doing a chroot() ***/
tzset();
/** Change our root directory **/
if ( dochroot ) {
if (chroot(Data_Dir)) {
Abortoutput(sockfd, "Data_Dir dissappeared!");
exit(-1);
}
uchdir("/"); /* needed after chroot */
}
if (setuid(uid)) {
LOGGopher(sockfd, "Can't set UID!");
Abortoutput(sockfd, "Can't set UID!");
exit(-1);
}
(void) signal(SIGALRM,read_timeout);
(void) alarm(READTIMEOUT);
if(setjmp(env)) {
LOGGopher(sockfd,"readline: Timed out!");
Abortoutput(sockfd,"readline: Timed out!");
exit(-1);
}
length = readline(sockfd, inputline, MAXLINE); /** Get the line **/
/** Disable the alarm signal **/
(void) alarm(0);
(void) signal(SIGALRM,SIG_IGN);
if (length <= 0) {
close(sockfd);
err_quit("getcommand: readline error");
}
ZapCRLF(inputline);
/*
* Decide if we're an HTML server or not...
*/
if (strncmp(inputline, "GET /", 5) == 0) {
UsingHTML = TRUE;
selstr = inputline+5;
/** Convert the hex things back to text... ***/
Fromhexstr(selstr, selstr);
} else
selstr = inputline;
if (RunIndex) {
/*** Run like the old gindexd thing. ***/
char tempstr[512];
uchdir("/");
strcpy(tempstr, Data_Dir);
strcat(tempstr, "\t");
if (*selstr == '\t')
strcat(tempstr, selstr+1);
else
strcat(tempstr, selstr);
strcpy(Data_Dir, "/");
if (DEBUG)
writestring(sockfd, tempstr);
Do_IndexTrans(sockfd, tempstr);
return(0);
}
/*** With the funky new capability system we can just check the
first letter, end decide what the object refers to. ***/
switch (*selstr) {
case '\0':
case '\t':
/*** The null capability, so it's not a file, probably wants
to talk to a directory server ***/
/*** we'll just do a "list" of the root directory, with no user
capability. ***/
listdir(sockfd, "/");
LOGGopher(sockfd, "Root Connection");
break;
case 'h':
/*** A raw html file ***/
/*** Turn off html'ing and just dump the file ***/
UsingHTML = FALSE;
case '0':
/*** It's a generic file capability ***/
printfile(sockfd, selstr+1, 0, -1);
/*** Log it ***/
strcpy(logline, "retrieved file ");
strcat(logline, selstr+1);
LOGGopher(sockfd, logline);
break;
case '1':
/*** It's a directory capability ***/
listdir(sockfd, selstr+1);
/** Log it **/
strcpy(logline, "retrieved directory ");
strcat(logline, selstr+1);
LOGGopher(sockfd, logline);
break;
case '7':
/*** It's an index capability ***/
if (Can_Search(sockfd) == FALSE) {
char tmpstr[256];
Abortoutput(sockfd, GDCgetBummerMsg(Config));
sprintf(tmpstr, "Denied access for %s", selstr+1);
LOGGopher(sockfd, tmpstr);
break;
}
Do_IndexTrans(sockfd, selstr+1);
break;
case 'I':
case '9':
/*** It's a binary thingie... ***/
/*** Okay, it's not a sound, but what the heck.... ***/
echosound(sockfd, selstr+1);
/* Log it */
strcpy(logline, "retrieved binary ");
strcat(logline, selstr+1);
LOGGopher(sockfd, logline);
break;
case 's':
/*** It's a sound capability ***/
echosound(sockfd, selstr+1);
/* Log it */
strcpy(logline, "retrieved sound ");
strcat(logline, selstr+1);
LOGGopher(sockfd, logline);
break;
case 'm':
/*** This is an internal identifier ***/
/*** The m paired with an Objtype of 1 makes a mail spool file
into a directory.
***/
if (Can_Browse(sockfd) == FALSE) {
char tmpstr[256];
Abortoutput(sockfd, GDCgetBummerMsg(Config));
sprintf(tmpstr, "Denied access for %s", selstr+1);
LOGGopher(sockfd, tmpstr);
break;
}
process_mailfile(sockfd, selstr + 1);
/** Log it **/
strcpy(logline, "retrieved maildir ");
strcat(logline, selstr+1);
LOGGopher(sockfd, logline);
break;
case 'R':
/*** This is an internal identifier ***/
/*** The R defines a range ****/
/*** The format is R<startbyte>-<endbyte>-<filename> **/
{
int startbyte, endbyte;
char *cp, *oldcp;
cp = strchr(selstr+1, '-');
if (cp == NULL) {
Abortoutput(sockfd, "Range specifier error");
break;
}
*cp = '\0';
startbyte = atoi(selstr+1);
oldcp = cp+1;
cp = strchr(oldcp, '-');
if (cp == NULL) {
Abortoutput(sockfd, "Range specifier error");
exit(-1);
}
*cp = '\0';
endbyte = atoi(oldcp);
oldcp = cp + 1;
if (DEBUG)
fprintf(stderr, "Start: %d, End: %d File: %s\n", startbyte, endbyte, oldcp);
writestring(sockfd, "This section is from the document '");
writestring(sockfd, oldcp);
writestring(sockfd, "'.\r\n\r\n");
printfile(sockfd, oldcp, startbyte, endbyte);
/*** Log it ***/
sprintf(logline, "retrieved range %d - %d of file %s", startbyte, endbyte, oldcp);
LOGGopher(sockfd, logline);
break;
}
case 'f':
if (Can_Browse(sockfd) == FALSE) {
char tmpstr[256];
Abortoutput(sockfd, GDCgetBummerMsg(Config));
sprintf(tmpstr, "Denied access for %s", selstr);
LOGGopher(sockfd, tmpstr);
break;
}
if (strncmp(selstr, "ftp:",4)==0){
sprintf(logline, "retrieved %s", selstr);
LOGGopher(sockfd, logline);
SendFtpQuery(sockfd, selstr+4);
TranslateResults(sockfd);
break;
}
break;
case 'e':
if (Can_Browse(sockfd) == FALSE) {
char tmpstr[256];
Abortoutput(sockfd, GDCgetBummerMsg(Config));
sprintf(tmpstr, "Denied access for %s", selstr);
LOGGopher(sockfd, tmpstr);
break;
}
if (strncmp(selstr, "exec:", 5)==0) {
/* args are between colons */
char *args, *command;
command = strrchr(selstr + 5, ':');
if (command == NULL)
break;
if (*(selstr+5) == ':' && *(selstr+6) == ':')
args = NULL;
else
args = selstr+5;
*command = '\0';
command++;
EXECargs = args;
printfile(sockfd, command, 0, -1);
}
break;
case 'w':
{
if (strncmp(selstr, "waissrc:", 8) == 0) {
if (Can_Search(sockfd) == FALSE) {
char tmpstr[256];
Abortoutput(sockfd, GDCgetBummerMsg(Config));
sprintf(tmpstr, "Denied access for %s", selstr);
LOGGopher(sockfd, tmpstr);
break;
}
SearchRemoteWAIS(sockfd, selstr+8);
break;
}
else if (strncmp(selstr, "waisdocid:", 10) == 0) {
if (Can_Browse(sockfd) == FALSE) {
char tmpstr[256];
Abortoutput(sockfd, GDCgetBummerMsg(Config));
sprintf(tmpstr, "Denied access for %s", selstr);
LOGGopher(sockfd, tmpstr);
break;
}
Fetchdocid(sockfd, selstr+10);
break;
}
}
default:
/*** Hmmm, must be an old link... Let's see if it exists ***/
switch (isadir(selstr)) {
case -1:
/* no such file */
sprintf(logline, "'%s' does not exist", selstr);
LOGGopher(sockfd, logline);
Abortoutput(sockfd, logline);
break;
case 0:
/* it's a file */
printfile(sockfd, selstr, 0, -1);
/* Log it... */
strcpy(logline, "retrieved file ");
strcat(logline, selstr);
LOGGopher(sockfd, logline);
break;
case 1:
/* it's a directory */
listdir(sockfd, selstr);
/* Log it */
strcpy(logline, "retrieved directory ");
strcat(logline, inputline);
LOGGopher(sockfd, logline);
break;
}
}
return(0);
}
/*
* Cache timeout value.
* If cache is less than secs seconds old, it's ok.
* Otherwise, compare time of cache to dir and all files in dir and dir/.cap.
* If cache is newest, it's ok, otherwise it must be rebuilt.
*
* Not really great for big directories, but better in general for smaller
* directories..
*/
boolean
Cachetimedout(cache, secs, dir)
char *cache;
int secs;
char *dir;
{
STATSTR buf;
int result;
time_t now;
result = rstat(cache, &buf);
if (result != 0)
return(-1);
time(&now);
if (DEBUG)
printf("Cache now: %d, cache file: %d", now,buf.st_mtime);
if ( now < (buf.st_mtime + secs))
return(FALSE);
else
return(TRUE);
}
/*
* Returns true (1) for a directory
* false (0) for a file
* -1 for anything else
*/
boolean
isadir(path)
char *path;
{
STATSTR buf;
int result;
result = rstat(path, &buf);
if (result != 0)
return(-1);
if (S_ISDIR(buf.st_mode)) {
if (! access(path, F_OK))
return(1);
else
return(-1);
}
else if (S_ISREG(buf.st_mode))
return(0);
else
return(-1);
}
/*
* This function tries to find out what type of file a pathname is.
* It then fills in the VAR type variables ObjType & ServerPath with
* corresponding info.
*/
void
Getfiletypes(newpath, filename, ObjType, ServerPath)
char *newpath;
char *filename;
char *ObjType;
char **ServerPath;
{
boolean dirresult;
int Zefilefd;
static char Zebuf[256];
char *cp;
static char Selstr[512];
if (ServerPath != NULL) /* Don't overwrite existing path if any */
*ServerPath = Selstr;
dirresult = isadir(filename);
if (dirresult == -1) { /** Symlink or Special **/
*ObjType = '3';
return;
}
if (dirresult == 1) {
*ObjType = '1';
*Selstr = '1';
strcpy(Selstr +1, newpath);
return;
}
else { /** Some kind of data file.... */
/*** The default is a generic text file ***/
*ObjType = '0';
*Selstr = '0';
strcpy(Selstr + 1, newpath);
/*** Test and see if the thing exists... and is readable ***/
if ((Zefilefd = ropen(filename, O_RDONLY)) < 0) {
*ObjType = '3';
return;
}
bzero(Zebuf, sizeof(Zebuf));
read(Zefilefd, Zebuf, sizeof(Zebuf));
close(Zefilefd);
/*** Check the first few bytes for sound data ***/
cp = Zebuf;
if (strncmp(cp, ".snd", 4)==0) {
*ObjType = 's';
*Selstr = 's';
strcpy(Selstr+1, newpath);
}
/*** Check and see if it's mailbox data ***/
if (is_mail_from_line(Zebuf)==0) {
*ObjType = '1';
*Selstr = 'm';
strcpy(Selstr+1, newpath);
}
/*** Check for uuencoding data ***/
if (strncmp(cp,"begin",6) == 0) {
*ObjType = '6';
*Selstr = '6';
strcpy(Selstr+1, newpath);
}
/*** Check for GIF magic code ***/
if (strncmp(cp, "GIF", 3) == 0) {
*ObjType = 'I';
*Selstr = 'I';
strcpy(Selstr + 1, newpath);
}
/*** Okay, now let's check for the stuff from gopherd.conf files ***/
{
char Gtype, *prefix;
if (GDCExtension(Config, filename, &Gtype, &prefix)) {
*ObjType = Gtype;
strcpy(Selstr, prefix);
strcpy(Selstr+strlen(prefix), newpath);
}
}
}
}
/*
** This function lists out what is in a particular directory.
** it also outputs the contents of link files.
**
** It also checks for the existance of a .cache file if caching is
** turned on...
**
** Ack is this ugly.
*/
void
listdir(sockfd, pathname)
int sockfd;
char *pathname;
{
DIR *ZeDir;
char sidename[256];
char filename[256];
static char newpath[512];
#ifdef DL
char dlpath[2]; /*** for DL**/
char *dlout;
#endif
FILE *SideFile;
static GopherStruct *Gopherp = NULL;
char Typep, *Pathp, *cachefile;
struct dirent *dp;
/*** Make our gopherobj ****/
if (Gopherp == NULL)
Gopherp = GSnew();
if (rchdir(pathname)<0) {
Abortoutput(sockfd, "- Cannot access that directory");
return;
}
if (UsingHTML)
cachefile = ".cache.html";
else
cachefile = ".cache";
if (Can_Browse(sockfd) == FALSE) {
char tmpstr[256];
Abortoutput(sockfd, GDCgetBummerMsg(Config));
sprintf(tmpstr, "Denied access for %s", pathname);
LOGGopher(sockfd, tmpstr);
return;
}
if (Caching && Cachetimedout(cachefile, CACHE_TIME, ".")==FALSE) {
/** Cache is still active, spit out the cache file
and get outta here..... **/
printfile(sockfd, cachefile, 0, -1);
return;
}
/** If we didn't cache then we have to use a sorting directory... **/
SortDir = GDnew(64);
/* open "." since we just moved there - makes it work when not
chroot()ing and using relative paths */
if ((ZeDir = ropendir(".")) == NULL) {
Abortoutput(sockfd, "Cannot get that directory");
return;
}
for (dp = readdir(ZeDir); dp != NULL; dp = readdir(ZeDir)) {
strcpy(newpath, pathname);
if (newpath[strlen(newpath)-1] != '/')
strcat(newpath, "/");
strcat(newpath, dp->d_name);
strcpy(sidename, "./.cap/");
strcpy(filename, dp->d_name);
strcat(sidename, dp->d_name);
if (filename[0] == '.' &&
isadir(filename)==0 &&
strncmp(filename, ".cache", 6) !=0) {
/*** This is a link file, let's process it ***/
int linkfd;
linkfd = uopen(filename, O_RDONLY);
if (linkfd >0) {
GDfromLink(SortDir, linkfd, Zehostname, GopherPort);
close(linkfd);
}
}
/** Only chew list out files that don't start with a dot **/
/** or aren't named dev, usr, bin, etc, or core. **/
if ((filename[0] != '.') && !GDCignore(Config, filename)) {
/** Check to see if there's a set-aside file with more info ***/
/** But first initialize the Gopherstruct **/
GSinit(Gopherp);
GSsetHost(Gopherp, Zehostname);
GSsetPort(Gopherp, GopherPort);
Typep = '\0';
Pathp = NULL;
Getfiletypes(newpath, filename, &Typep, &Pathp);
if (Typep =='3')
continue;
GSsetType(Gopherp, Typep);
GSsetPath(Gopherp, Pathp);
if (GSgetTitle(Gopherp) == NULL) {
/*** Check to see if we have a compressed file ***/
if (strcmp(filename + strlen(filename) -2, ".Z") ==0 &&
strcmp(filename + strlen(filename) -6, ".tar.Z") != 0)
filename[strlen(filename) - 2] = '\0';
GSsetTitle(Gopherp, filename);
}
else
GSsetTitle(Gopherp, filename);
if ((SideFile = rfopen(sidename, "r"))!=0) {
if (DEBUG == TRUE)
printf("Side file name: %s\n", sidename);
Process_Side(SideFile, Gopherp);
}
#ifdef DL
/* Process a "dl" description if there is one! */
dlpath[0] = '.';
dlpath[1] = '\0';
dlout = getdesc(NULL,dlpath,filename,0);
if (DEBUG)
printf("dl: %s %s %s\n", dlpath, filename, dlout);
if (dlout != NULL) {
GSsetTitle(Gopherp, dlout);
}
#endif
/*** Add the entry to the directory ***/
GDaddGS(SortDir, Gopherp);
}
}
GDsort(SortDir);
if (UsingHTML) {
int aboutfd;
aboutfd = uopen(".about.html", O_RDONLY);
if (aboutfd > 0) {
while (readline(aboutfd, newpath, 512))
writestring(sockfd, newpath);
close(aboutfd);
}
GDtoNetHTML(SortDir, sockfd);
}
else {
GDtoNet(SortDir, sockfd);
writestring(sockfd, ".\r\n");
}
/*
* Write out the cache... *After* we send out the data to the net.
*/
if (Caching) {
int cachefd;
cachefd = uopen(cachefile, O_WRONLY|O_CREAT|O_TRUNC, 0755);
if (cachefd != 0) {
if (DEBUG) {
printf("Caching directory...\n");
}
if (UsingHTML) {
int aboutfd;
aboutfd = uopen(".about.html", O_RDONLY);
if (aboutfd > 0) {
while (readline(aboutfd, newpath, 512))
writestring(cachefd, newpath);
close(aboutfd);
}
GDtoNetHTML(SortDir, cachefd);
}
else
GDtoNet(SortDir, cachefd);
close(cachefd);
}
}
closedir(ZeDir);
}
/*
* This processes a file containing any subset of
* Type, Name, Path, Port or Host, and returns pointers to the
* overriding data that it finds.
*
* The caller may choose to initialise the pointers - so we don't
* touch them unless we find an over-ride.
*/
void
Process_Side(sidefile, Gopherp)
FILE *sidefile;
GopherObj *Gopherp;
{
char inputline[MAXLINE];
char *cp;
inputline[0] = '\0';
for (;;) {
for (;;) {
cp = fgets(inputline, 1024, sidefile);
if (inputline[0] != '#' || cp == NULL)
break;
}
/*** Test for EOF ***/
if (cp==NULL)
break;
ZapCRLF(inputline); /* should zap tabs as well! */
/*** Test for the various field values. **/
if (strncmp(inputline, "Type=", 5)==0) {
GSsetType(Gopherp, inputline[5]);
if (inputline[5] == '7') {
/*** Might as well set the path too... ***/
*(GSgetPath(Gopherp)) = '7';
}
if (inputline[5] == '9') {
/*** Might as well set the path too... ***/
*(GSgetPath(Gopherp)) = '9';
}
}
else if (strncmp(inputline, "Name=", 5)==0) {
GSsetTitle(Gopherp, inputline+5);
}
else if (strncmp(inputline, "Host=", 5)==0) {
GSsetHost(Gopherp, inputline+5);
}
else if (strncmp(inputline, "Port=", 5)==0) {
GSsetPort(Gopherp, atoi(inputline+5));
}
else if (strncmp(inputline, "Path=", 5)==0) {
GSsetPath(Gopherp, inputline+5);
}
else if (strncmp(inputline, "Numb=", 5)==0) {
GSsetNum(Gopherp, atoi(inputline+5));
}
else if (strncmp(inputline, "Name=", 5)==0) {
GSsetTitle(Gopherp, inputline+5);
}
}
fclose(sidefile);
}
/*
** This function opens the specified file, starts a zcat if needed,
** and barfs the file across the socket.
**
** It now also checks and sees if access is allowed
**
**
*/
void
printfile(sockfd, pathname, startbyte, endbyte)
int sockfd;
char *pathname;
int startbyte, endbyte;
{
FILE *ZeFile;
char inputline[512];
/*** Check and see if the peer has permissions to read files ***/
if (Can_Read(sockfd) == FALSE) {
char tmpstr[256];
if (writestring(sockfd, GDCgetBummerMsg(Config)) <0)
LOGGopher(sockfd, "Client went away"), exit(-1);
writestring(sockfd, "\r\nBummer.....\r\n.\r\n");
sprintf(tmpstr, "Denied access for %s", pathname);
LOGGopher(sockfd, tmpstr);
return;
}
if (UsingHTML && strcmp(pathname, ".cache.html") != 0) {
writestring(sockfd, "<XMP>\r\n");
}
if ( (ZeFile = rfopen(pathname, "r")) == NULL) {
/*
* The specified file does not exist
*/
char notexistline[256];
sprintf(notexistline, "'%s' does not exist!!", pathname);
Abortoutput(sockfd, notexistline);
return;
}
if (startbyte != 0)
fseek(ZeFile, startbyte, 0);
{
FILE *pp;
if (pp = specialfile(ZeFile, pathname)) {
fclose(ZeFile);
ZeFile = pp;
}
}
while (fgets(inputline, MAXLINE, ZeFile) != NULL) {
ZapCRLF(inputline);
/** Period on a line by itself, double it.. **/
if (*inputline == '.' && inputline[1] == '\0') {
inputline[1] = '.';
inputline[2] = '\0';
}
strcat(inputline, "\r\n");
if (writestring(sockfd, inputline) <0)
LOGGopher(sockfd, "Client went away"), exit(-1);
if (endbyte >0) {
if (ftell(ZeFile) >= endbyte)
break;
}
}
Specialclose(ZeFile);
if (UsingHTML) {
writestring(sockfd, "</XMP>\r\n");
}
if (writestring(sockfd, ".\r\n")<0)
LOGGopher(sockfd, "Client went away"), exit(-1);
}
#define BUFSIZE 1523 /* A pretty good value for ethernet */
void
echosound(sockfd, filename)
int sockfd;
char *filename;
{
FILE *sndfile;
unsigned char in[BUFSIZE];
register int j;
int gotbytes;
if (Can_Read(sockfd) == FALSE) {
char tmpstr[256];
if (writestring(sockfd, GDCgetBummerMsg(Config)) <0)
LOGGopher(sockfd, "Client went away"), exit(-1);
writestring(sockfd, "\r\nBummer.....\r\n.\r\n");
sprintf(tmpstr, "Denied access for %s", filename);
LOGGopher(sockfd, tmpstr);
return;
}
if (strcmp(filename, "-") == 0) {
/*** Do some live digitization!! **/
sndfile = popen("record -", "r");
}
else
sndfile = rfopen(filename, "r");
while(1) {
gotbytes = fread(in, 1, BUFSIZE, sndfile);
if (gotbytes == 0)
break; /*** end of file or error... ***/
j = writen(sockfd, in, gotbytes);
if (j == 0)
break; /*** yep another error condition ***/
}
}